using System.Runtime.InteropServices;

namespace Making.Avalonia.WebView.MacOS.Native;

internal static class WKWebViewInterop
{
    private const string WebKitFramework = "/System/Library/Frameworks/WebKit.framework/WebKit";
    private const string FoundationFramework = "/System/Library/Frameworks/Foundation.framework/Foundation";
    private const string AppKitFramework = "/System/Library/Frameworks/AppKit.framework/AppKit";

    // WKWebView class methods
    [DllImport(WebKitFramework, EntryPoint = "objc_msgSend")]
    public static extern IntPtr objc_msgSend_IntPtr(IntPtr receiver, IntPtr selector);

    [DllImport(WebKitFramework, EntryPoint = "objc_msgSend")]
    public static extern void objc_msgSend_void(IntPtr receiver, IntPtr selector);

    [DllImport(WebKitFramework, EntryPoint = "objc_msgSend")]
    public static extern IntPtr objc_msgSend_IntPtr_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1);

    [DllImport(WebKitFramework, EntryPoint = "objc_msgSend")]
    public static extern void objc_msgSend_void_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1);

    [DllImport(WebKitFramework, EntryPoint = "objc_msgSend")]
    public static extern double objc_msgSend_double(IntPtr receiver, IntPtr selector);

    [DllImport(WebKitFramework, EntryPoint = "objc_msgSend")]
    public static extern void objc_msgSend_void_double(IntPtr receiver, IntPtr selector, double value);

    [DllImport(WebKitFramework, EntryPoint = "objc_msgSend")]
    public static extern bool objc_msgSend_bool(IntPtr receiver, IntPtr selector);

    // Objective-C runtime
    [DllImport(FoundationFramework, EntryPoint = "objc_getClass")]
    public static extern IntPtr objc_getClass(string className);

    [DllImport(FoundationFramework, EntryPoint = "sel_registerName")]
    public static extern IntPtr sel_registerName(string selectorName);

    [DllImport(FoundationFramework, EntryPoint = "objc_msgSend")]
    public static extern IntPtr objc_msgSend_NSString(IntPtr receiver, IntPtr selector, IntPtr nsString);

    // NSString creation
    [DllImport(FoundationFramework, EntryPoint = "objc_msgSend")]
    public static extern IntPtr NSString_stringWithUTF8String(IntPtr receiver, IntPtr selector, string utf8String);

    // JavaScript execution with completion handler
    [DllImport(WebKitFramework, EntryPoint = "objc_msgSend")]
    public static extern void objc_msgSend_void_IntPtr_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1, IntPtr arg2);

    // Block runtime functions for completion handlers
    [DllImport(FoundationFramework, EntryPoint = "_Block_copy")]
    public static extern IntPtr Block_copy(IntPtr block);

    [DllImport(FoundationFramework, EntryPoint = "_Block_release")]
    public static extern void Block_release(IntPtr block);

    // NSString UTF8String method
    [DllImport(FoundationFramework, EntryPoint = "objc_msgSend")]
    public static extern IntPtr objc_msgSend_IntPtr_UTF8String(IntPtr receiver, IntPtr selector);

    public static readonly IntPtr sel_UTF8String = sel_registerName("UTF8String");
    public static readonly IntPtr sel_description = sel_registerName("description");

    // Selectors
    public static readonly IntPtr sel_alloc = sel_registerName("alloc");
    public static readonly IntPtr sel_init = sel_registerName("init");
    public static readonly IntPtr sel_initWithFrame = sel_registerName("initWithFrame:");
    public static readonly IntPtr sel_loadRequest = sel_registerName("loadRequest:");
    public static readonly IntPtr sel_loadHTMLString = sel_registerName("loadHTMLString:baseURL:");
    public static readonly IntPtr sel_goBack = sel_registerName("goBack");
    public static readonly IntPtr sel_goForward = sel_registerName("goForward");
    public static readonly IntPtr sel_reload = sel_registerName("reload");
    public static readonly IntPtr sel_canGoBack = sel_registerName("canGoBack");
    public static readonly IntPtr sel_canGoForward = sel_registerName("canGoForward");
    public static readonly IntPtr sel_evaluateJavaScript = sel_registerName("evaluateJavaScript:completionHandler:");
    public static readonly IntPtr sel_setMagnification = sel_registerName("setMagnification:");
    public static readonly IntPtr sel_magnification = sel_registerName("magnification");
    public static readonly IntPtr sel_stringWithUTF8String = sel_registerName("stringWithUTF8String:");
    public static readonly IntPtr sel_requestWithURL = sel_registerName("requestWithURL:");
    public static readonly IntPtr sel_URLWithString = sel_registerName("URLWithString:");
    
    // Classes
    public static readonly IntPtr class_WKWebView = objc_getClass("WKWebView");
    public static readonly IntPtr class_NSString = objc_getClass("NSString");
    public static readonly IntPtr class_NSURL = objc_getClass("NSURL");
    public static readonly IntPtr class_NSURLRequest = objc_getClass("NSURLRequest");

    // Structs
    [StructLayout(LayoutKind.Sequential)]
    public struct CGRect
    {
        public double X, Y, Width, Height;
        
        public CGRect(double x, double y, double width, double height)
        {
            X = x;
            Y = y;
            Width = width;
            Height = height;
        }
    }

    // Block descriptor structure for Objective-C blocks
    [StructLayout(LayoutKind.Sequential)]
    public struct BlockDescriptor
    {
        public IntPtr reserved;
        public IntPtr size;
        public IntPtr copy_helper;
        public IntPtr dispose_helper;
        public IntPtr signature;
    }

    // Block literal structure
    [StructLayout(LayoutKind.Sequential)]
    public struct BlockLiteral
    {
        public IntPtr isa;
        public int flags;
        public int reserved;
        public IntPtr invoke;
        public IntPtr descriptor;
        public IntPtr captured;
    }

    // JavaScript completion handler delegate
    public delegate void JavaScriptCompletionHandler(IntPtr result, IntPtr error);

    // Helper methods
    public static IntPtr CreateNSString(string str)
    {
        return NSString_stringWithUTF8String(class_NSString, sel_stringWithUTF8String, str);
    }

    public static IntPtr CreateNSURL(string urlString)
    {
        var nsString = CreateNSString(urlString);
        return objc_msgSend_IntPtr_IntPtr(class_NSURL, sel_URLWithString, nsString);
    }

    public static IntPtr CreateNSURLRequest(IntPtr url)
    {
        return objc_msgSend_IntPtr_IntPtr(class_NSURLRequest, sel_requestWithURL, url);
    }

    // Create an Objective-C block for JavaScript completion handler
    public static IntPtr CreateJavaScriptCompletionBlock(JavaScriptCompletionHandler handler)
    {
        // This is a simplified block creation
        // In a production implementation, we'd need proper block runtime setup
        var blockPtr = Marshal.AllocHGlobal(Marshal.SizeOf<BlockLiteral>());
        
        var blockLiteral = new BlockLiteral
        {
            isa = GetBlockClass(),
            flags = 0x40000000, // BLOCK_HAS_SIGNATURE
            reserved = 0,
            invoke = Marshal.GetFunctionPointerForDelegate(handler),
            descriptor = CreateBlockDescriptor(),
            captured = IntPtr.Zero
        };
        
        Marshal.StructureToPtr(blockLiteral, blockPtr, false);
        return Block_copy(blockPtr);
    }

    private static IntPtr GetBlockClass()
    {
        return objc_getClass("NSBlock");
    }

    private static IntPtr CreateBlockDescriptor()
    {
        var descriptorPtr = Marshal.AllocHGlobal(Marshal.SizeOf<BlockDescriptor>());
        var descriptor = new BlockDescriptor
        {
            reserved = IntPtr.Zero,
            size = new IntPtr(Marshal.SizeOf<BlockLiteral>()),
            copy_helper = IntPtr.Zero,
            dispose_helper = IntPtr.Zero,
            signature = CreateNSString("v@?@@") // void (^)(id, NSError*)
        };
        
        Marshal.StructureToPtr(descriptor, descriptorPtr, false);
        return descriptorPtr;
    }

    // Extract string from JavaScript result
    public static string? ExtractStringFromJSResult(IntPtr result)
    {
        if (result == IntPtr.Zero)
            return null;

        var descriptionPtr = objc_msgSend_IntPtr(result, sel_description);
        if (descriptionPtr == IntPtr.Zero)
            return null;

        var utf8Ptr = objc_msgSend_IntPtr_UTF8String(descriptionPtr, sel_UTF8String);
        if (utf8Ptr == IntPtr.Zero)
            return null;

        return Marshal.PtrToStringAnsi(utf8Ptr);
    }
}